﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;

namespace MaterialEditor
{
    public class CubeRender : System.Windows.Forms.Panel
    {
        private Effect effect;

        private Vector2 rotation;

        Color[] data = new Color[4000 * 4000];

        private Texture2D blankTexture;

        private Texture2D _texture;
        public Texture2D Texture
        {
            get { return _texture; }

            set
            {
                value.GetData(data, 0, value.Width * value.Height);
                _texture = new Texture2D(graphics.device, value.Width, value.Height);
                _texture.SetData(data, 0, value.Width * value.Height);
            }
        }

        private Texture2D _normalMap;
        public Texture2D NormalMap
        {
            get { return _normalMap; }

            set
            {
                value.GetData(data, 0, value.Width * value.Height);
                _normalMap = new Texture2D(graphics.device, value.Width, value.Height);
                _normalMap.SetData(data, 0, value.Width * value.Height);
            }
        }

        private PositionNormalTextureTangent[] vertecies;

        private Graphics graphics;

        public CubeRender()
        {
        }

        public void Inilitize()
        {

            if (!DesignMode) // do not execute if in design mode
            {
                // allocate a bunch of data to create the cube
                Vector3[] points =
	            {
		            new Vector3(-0.5f, -0.5f, -0.5f),
		            new Vector3(0.5f, -0.5f, -0.5f),
		            new Vector3(-0.5f, 0.5f, -0.5f),
		            new Vector3(0.5f, 0.5f, -0.5f),
		            new Vector3(-0.5f, -0.5f, 0.5f),
		            new Vector3(0.5f, -0.5f, 0.5f),
		            new Vector3(-0.5f, 0.5f, 0.5f),
		            new Vector3(0.5f, 0.5f, 0.5f),
	            };

                Vector2[] texCoords = 
	            {		
		            new Vector2(0.000000f, 0.000000f),
		            new Vector2(1.000000f, 0.000000f),
		            new Vector2(1.000000f, 0.000000f),
		            new Vector2(0.000000f, 0.000000f),
		            new Vector2(0.000000f, 1.000000f),
		            new Vector2(1.000000f, 1.000000f),
		            new Vector2(0.000000f, 1.000000f),
		            new Vector2(1.000000f, 1.000000f), 
		            new Vector2(0.000000f, 0.000000f),
		            new Vector2(1.000000f, 0.000000f),
		            new Vector2(0.000000f, 1.000000f),
		            new Vector2(1.000000f, 1.000000f),
		            new Vector2(0.000000f, 0.000000f),
		            new Vector2(1.000000f, 0.000000f),
		            new Vector2(1.000000f, 1.000000f),
		            new Vector2(0.000000f, 1.000000f),
		            new Vector2(0.000000f, 0.000000f),
		            new Vector2(0.000000f, 1.000000f),
		            new Vector2(1.000000f, 0.000000f),
		            new Vector2(1.000000f, 1.000000f),
	            };

                int[] vert_indicies =
	            {
		            0, 3, 1, 
		            0, 5, 4,
		            0, 6, 2,
		            1, 7, 5,
		            2, 7, 3,
		            4, 7, 6,
		            2, 3, 0, 
		            1, 5, 0,
		            4, 6, 0, 
		            3, 7, 1,
		            6, 7, 2,
		            5, 7, 4
	            };

                int[] texCoord_Indicies =
	            {
		            2, 7, 4,
		            1, 12, 11,
		            1, 15, 5,
		            3, 18, 13,
		            5, 19, 8,
		            9, 20, 16,
		            6, 7, 2,
		            3, 12, 1,
		            10, 15, 1,
		            8, 18, 3,
		            17, 19, 5,
		            14, 20, 9,
	            };

                Vector3[] normals = new Vector3[36];
                Vector4[] tangents = new Vector4[36 * 2];
                for (int i = 0; i < 36; i += 3)
                {
                    Vector3 mainVertex = points[vert_indicies[i]];
                    Vector3 Q1 = mainVertex - points[vert_indicies[i + 1]];
                    Vector3 Q2 = mainVertex - points[vert_indicies[i + 2]];

                    Vector2 st1 = texCoords[texCoord_Indicies[i] - 1];
                    Vector2 st2 = texCoords[texCoord_Indicies[i + 1] - 1];

                    float s1 = st1.X;
                    float s2 = st2.X;
                    float t1 = st1.Y;
                    float t2 = st2.Y;

                    // compute the normal for this face
                    Vector3 normal = Vector3.Cross(Q1, Q2);

                    // comput the tangent for this face
                    float f = 1.0f / (s1 * t2 - s2 - t1);
                    Vector4 tangent = Vector4.Zero, bitangent = Vector4.Zero;
                    tangent.X = f * (t2 * Q1.X - t1 * Q2.X);
                    tangent.Y = f * (t2 * Q1.Y - t1 * Q2.Y);
                    tangent.Z = f * (t2 * Q1.Z - t1 * Q2.Z);

                    bitangent.X = f * (-s2 * Q1.X + s1 * Q2.X);
                    bitangent.Y = f * (-s2 * Q1.Y + s1 * Q2.Y);
                    bitangent.Z = f * (-s2 * Q1.Z + s1 * Q2.Z);

                    tangents[i] = tangents[i + 1] = tangents[i + 2] = tangent;
                    tangents[i * 2] = tangents[i * 2 + 1] = tangents[i * 2 + 2] = bitangent;
                    normals[i] = normals[i + 1] = normals[i + 2] = i % 2 == 0 ? normal : -normal;
                }

                    // create cube vertex buffer
                    vertecies = new PositionNormalTextureTangent[36];
                for (int i = 0; i < 36; i++)
                {
                    vertecies[i] = new PositionNormalTextureTangent(points[vert_indicies[i]], normals[i], tangents[i], texCoords[texCoord_Indicies[i] - 1]);
                }

                // ortho-normalize tangents
                for (int i = 0; i < 36; i++)
                {
                    Vector3 normal = vertecies[i].normal;
                    Vector4 tangent = vertecies[i].tangent;

                    vertecies[i].tangent = (tangent - normal.ToVector4() * Vector4.Dot(tangent, normal.ToVector4()));
                    vertecies[i].tangent.Normalize();

                    // calculate handedness
                    vertecies[i].tangent.W = Vector3.Dot(Vector3.Cross(tangent.ToVector3(), tangents[i * 2].ToVector3()), tangent.ToVector3()) < 0.0f ? -1.0f : 1.0f;
                }

                //////////////////////////////////////////////////////////////////////////////
                // inilitize graphics and shaders
                graphics = Graphics.Inilitize(this);


                // create content manager to load shader
                ContentBuilder content = new ContentBuilder();
                content.Add(Environment.CurrentDirectory + "//Content//AdsLight.fx", "AdsLight", "EffectImporter", "EffectProcessor");
                ServiceContainer services = new ServiceContainer();
                services.AddService<IGraphicsDeviceService>(new GraphicsDeviceService(graphics.device));
                content.Build();
                ContentManager manager = new ContentManager(services, content.OutputDirectory);
                effect = manager.Load<Effect>("AdsLight");

                // set up effect matricies
                effect.Parameters["Projection"].SetValue(Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(65.0f), graphics.AspectRatio, 0.1f, 1000.0f));
                effect.Parameters["View"].SetValue(Matrix.CreateLookAt(Vector3.Backward * 2.0f, Vector3.Zero, Vector3.Up));
                effect.Parameters["lightPosition"].SetValue(Vector3.Backward * 4);

                // create a blank texture
                blankTexture = new Texture2D(graphics.device, 1, 1);
                blankTexture.SetData(new Color[] { Color.Black });

            }
        }

        Vector2 mousePosPrev;
        public void Update(float dt)
        {
            lightRotation += MathHelper.ToRadians(1.0f);
            if (Mouse.GetState().RightButton == ButtonState.Pressed)
            {
                rotation.X += MathHelper.ToRadians(Mouse.GetState().X - mousePosPrev.X);
                rotation.Y += MathHelper.ToRadians(Mouse.GetState().Y - mousePosPrev.Y);
            }

            effect.Parameters["lightPosition"].SetValue(new Vector3((float)Math.Cos(lightRotation), 0.0f, (float)Math.Sin(lightRotation)));
            effect.Parameters["World"].SetValue(Matrix.CreateFromYawPitchRoll(rotation.X, rotation.Y, 0.0f));
            mousePosPrev = new Vector2(Mouse.GetState().X, Mouse.GetState().Y);
        }

        float lightRotation;
        public void Draw()
        {
            graphics.device.Clear(Color.CornflowerBlue);
            graphics.device.Textures[0] = Texture == null ? blankTexture : Texture;
            graphics.device.Textures[1] = NormalMap == null ? blankTexture : NormalMap;
            graphics.device.SamplerStates[0] = SamplerState.LinearWrap;
            effect.CurrentTechnique.Passes[0].Apply();
            effect.Parameters["useNormalMap"].SetValue(NormalMap != null);
            graphics.device.DrawUserPrimitives(PrimitiveType.TriangleList, vertecies, 0, 12);
            graphics.Present();
        }
    }
}